package org.walkerljl.commons.designer;

import org.walkerljl.commons.log.Logger;
import org.walkerljl.commons.log.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Task<Event> implements Runnable, Cloneable {

    private static final Logger LOGGER = LoggerFactory.getLogger(Task.class);

    private final EventHandler<Event> eventHandler;
    private final List<EventHandler<Event>> dependedEventHandlers;
    protected TaskExecuteEngine<Event> taskExecuteEngine;
    private volatile int unsatisfiedDepdendings;
    private Event event;

    private Lock lock;

    Task(EventHandler<Event> eventHandler, int depdending,
         List<EventHandler<Event>> dependedEventHandlers) {
        this.eventHandler = eventHandler;
        this.unsatisfiedDepdendings = depdending;
        this.dependedEventHandlers = dependedEventHandlers;
    }

    void init(TaskExecuteEngine<Event> taskExecuteEngine, Event event) {
        this.taskExecuteEngine = taskExecuteEngine;
        this.event = event;
    }

    public void run() {
        try {
            eventHandler.onEvent(event);
            if (dependedEventHandlers != null) {
                List<Task<Event>> readyTasks = new ArrayList<Task<Event>>(dependedEventHandlers.size());
                for (int i = 0; i < dependedEventHandlers.size(); i++) {
                    Task<Event> task = taskExecuteEngine
                            .getTask(dependedEventHandlers.get(i));
                    if (task.decreaseUnsatisfiedDependcies() == 0) {
                        readyTasks.add(task);
                    }
                }
                if (readyTasks.size() > 0) {
                    for (int i = (readyTasks.size() - 1); i > 0; i--) {
                        taskExecuteEngine.executeTask(readyTasks.get(i));
                    }
                    readyTasks.get(0).run();
                }
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(String.format("[%s] has executed task [%s].", eventHandler.toString(), event.toString()));
            }
        } catch (Throwable e) {
            taskExecuteEngine.markAsError(e);
            LOGGER.error(String.format("[%s] fail to execute task [%s].", eventHandler.toString(), event.toString()));
        }
    }

    boolean hasUnsatisfiedDependcies() {
        lock.lock();
        try {
            return unsatisfiedDepdendings != 0;
        } finally {
            lock.unlock();
        }
    }

    private int decreaseUnsatisfiedDependcies() {
        lock.lock();
        try {
            return --unsatisfiedDepdendings;
        } finally {
            lock.unlock();
        }
    }

    @SuppressWarnings("unchecked")
    public Object clone() {
        try {
            Task<Event> cloned = (Task<Event>) super.clone();
            cloned.lock = new ReentrantLock();
            return cloned;
        } catch (Exception e) {
            throw new InternalError();
        }
    }
}
